#include "dev_sdram.h"

static SDRAM_HandleTypeDef SDRAM_Handler;

/* 向 SDRAM 发送命令 */
static RET_STATUS s_dev_sdram_send_cmd(uint8_t bank_idx, uint8_t cmd, uint8_t refresh_nbr, uint8_t reg_val)
{
    uint8_t                  target_bank;
    FMC_SDRAM_CommandTypeDef command;

    /* 判断命令是发给哪个内存块 */
    if (bank_idx == DEV_SDRAM_BANK1)
    {
        target_bank = FMC_SDRAM_CMD_TARGET_BANK1;
    }
    else if (bank_idx == DEV_SDRAM_BANK2)
    {
        target_bank = FMC_SDRAM_CMD_TARGET_BANK2;
    }
    else if (bank_idx == DEV_SDRAM_BANK1_2)
    {
        target_bank = FMC_SDRAM_CMD_TARGET_BANK1_2;
    }
    else
    {
        return RET_ERROR;    /* 发送失败 */
    }

    /* 发送命令 */
    command.CommandMode            = cmd;           /* 命令 */
    command.CommandTarget          = target_bank;   /* 目标SDRAM存储区域 */
    command.AutoRefreshNumber      = refresh_nbr;   /* 自刷新次数 */
    command.ModeRegisterDefinition = reg_val;       /* 要写入模式寄存器的值 */
    
    if (HAL_SDRAM_SendCommand(&SDRAM_Handler, &command, 0x1000) == HAL_OK) /* 向SDRAM发送命令 */
    {
        return RET_OK;
    }

    return RET_ERROR;
}

/* 发送SDRAM初始化序列 */
static void s_dev_sdram_initialization_sequence(SDRAM_HandleTypeDef *hsdram)
{
    uint32_t temp = 0;

    //SDRAM控制器初始化完成以后还需要按照如下顺序初始化SDRAM
    s_dev_sdram_send_cmd(0, FMC_SDRAM_CMD_CLK_ENABLE, 1, 0); //时钟配置使能
    HAL_Delay(100);                                          //至少延时200us
    s_dev_sdram_send_cmd(0, FMC_SDRAM_CMD_PALL, 1, 0);       //对所有存储区预充电
    s_dev_sdram_send_cmd(0, FMC_SDRAM_CMD_AUTOREFRESH_MODE, 8, 0);//设置自刷新次数 

    //配置模式寄存器,SDRAM的bit0~bit2为指定突发访问的长度，
    //bit3为指定突发访问的类型，bit4~bit6为CAS值，bit7和bit8为运行模式
    //bit9为指定的写突发模式，bit10和bit11位保留位
    temp=(uint32_t) SDRAM_MODEREG_BURST_LENGTH_1          |   //设置突发长度:1(可以是1/2/4/8)
                    SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |   //设置突发类型:连续(可以是连续/交错)
                    SDRAM_MODEREG_CAS_LATENCY_3           |   //设置CAS值:3(可以是2/3)
                    SDRAM_MODEREG_OPERATING_MODE_STANDARD |   //设置操作模式:0,标准模式
                    SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;     //设置突发写模式:1,单点访问

    s_dev_sdram_send_cmd(0, FMC_SDRAM_CMD_LOAD_MODE, 1, temp);   //设置SDRAM的模式寄存器

    /* Set the refresh rate counter */
    /* (15.62 us x Freq) - 20 */
    /* Set the device refresh counter */
    HAL_SDRAM_ProgramRefreshRate(hsdram, 683); 

    return;
}

void dev_sdram_init(void)
{
    FMC_SDRAM_TimingTypeDef SDRAM_Timing;

    /** Perform the SDRAM1 memory initialization sequence
    */
    SDRAM_Handler.Instance                = FMC_SDRAM_DEVICE;
    /* SDRAM_Handler.Init */
    SDRAM_Handler.Init.SDBank             = FMC_SDRAM_BANK1;                    /* 第一个SDRAM BANK */
    SDRAM_Handler.Init.ColumnBitsNumber   = FMC_SDRAM_COLUMN_BITS_NUM_9;        /* 列数量 */
    SDRAM_Handler.Init.RowBitsNumber      = FMC_SDRAM_ROW_BITS_NUM_13;          /* 行数量 */
    SDRAM_Handler.Init.MemoryDataWidth    = FMC_SDRAM_MEM_BUS_WIDTH_16;         /* 数据宽度为16位 */
    SDRAM_Handler.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;       /* 一共4个BANK */
    SDRAM_Handler.Init.CASLatency         = FMC_SDRAM_CAS_LATENCY_3;            /* CAS为3 */
    SDRAM_Handler.Init.WriteProtection    = FMC_SDRAM_WRITE_PROTECTION_DISABLE; /* 失能写保护 */
    SDRAM_Handler.Init.SDClockPeriod      = FMC_SDRAM_CLOCK_PERIOD_2;           /* SDRAM时钟为HCLK/2=180M/2=90M=11.1ns */
    SDRAM_Handler.Init.ReadBurst          = FMC_SDRAM_RBURST_ENABLE;            /* 使能突发读取 */
    SDRAM_Handler.Init.ReadPipeDelay      = FMC_SDRAM_RPIPE_DELAY_1;            /* 读通道延时 */
    /* SDRAM_Timing */
    SDRAM_Timing.LoadToActiveDelay    = 2;      /* 加载模式寄存器到激活时间的延迟为2个时钟周期 */
    SDRAM_Timing.ExitSelfRefreshDelay = 8;      /* 退出自刷新延迟为8个时钟周期 */
    SDRAM_Timing.SelfRefreshTime      = 6;      /* 自刷新时间为6个时钟周期 */
    SDRAM_Timing.RowCycleDelay        = 6;      /* 行循环延迟为6个时钟周期 */
    SDRAM_Timing.WriteRecoveryTime    = 2;      /* 恢复延迟为2个时钟周期 */
    SDRAM_Timing.RPDelay              = 2;      /* 行预充电延迟为2个时钟周期 */
    SDRAM_Timing.RCDDelay             = 2;      /* 行到列延迟为2个时钟周期 */

    if (HAL_SDRAM_Init(&SDRAM_Handler, &SDRAM_Timing) != HAL_OK)
    {
        Error_Handler( );
    }

    /* 发送SDRAM初始化序列 */
    s_dev_sdram_initialization_sequence(&SDRAM_Handler);

    return;
}

/* 在指定地址(write_addr + DEV_SDRAM_BANK_ADDR)开始, 连续写入len个字节 */
RET_STATUS dev_sdram_write(uint8_t *pbuf, uint32_t write_addr, uint32_t len)
{
    /* 检验参数有效性 */
    if ((pbuf == NULL) || (write_addr >= 0xFFFFFFFF))
    {
        return RET_ERROR;
    }

    for (; len != 0; len--)
    {
        *(volatile uint8_t *) (DEV_SDRAM_BANK_ADDR + write_addr) = *pbuf;
        write_addr++;
        pbuf++;
    }

    return RET_OK;
}

/* 在指定地址(read_addr + DEV_SDRAM_BANK_ADDR)开始, 连续读出len个字节 */
RET_STATUS dev_sdram_read(uint8_t *pbuf, uint32_t read_addr, uint32_t len)
{
    /* 检验参数有效性 */
    if ((pbuf == NULL) || (read_addr >= 0xFFFFFFFF))
    {
        return RET_ERROR;
    }

    for (; len != 0; len--)
    {
        *pbuf = *(volatile uint8_t *) (DEV_SDRAM_BANK_ADDR + read_addr);
        read_addr++;
        pbuf++;
    }
    
    return RET_OK;
}

/* SDRAM 测试程序 */
void dev_sdram_test(void)
{
    uint32_t i          = 0;
    uint32_t value      = 1;
    uint32_t last_value = 0;
    uint32_t t_start    = 0;
    uint32_t t_end      = 0;
    uint32_t *pBuf;
    uint32_t size = ((1024 * 1024 * 32) / 4);   /* 测试 8MB */
    
    pBuf = (uint32_t *) DEV_SDRAM_BANK_ADDR;
    rt_kprintf("=== SDRAM TEST === start write\r\n");
    rt_kprintf("write start tick: %d\r\n", t_start = HAL_GetTick());

    for (i = 0; i < (size / 8); i++)
    {
        *pBuf++ = value++;
        *pBuf++ = value++;
        *pBuf++ = value++;
        *pBuf++ = value++;
        *pBuf++ = value++;
        *pBuf++ = value++;
        *pBuf++ = value++;
        *pBuf++ = value++;
    }

    rt_kprintf("write end tick: %d\r\n", t_end = HAL_GetTick());
    rt_kprintf("write spend time: %dms\r\n", (t_end - t_start));
    rt_kprintf("write speed: %dMbyte/s\r\n", ((32 * 1000) / (t_end - t_start)));

    rt_kprintf("=== SDRAM TEST === start read\r\n");
    rt_kprintf("read start tick: %d\r\n", t_start = HAL_GetTick());

    pBuf = (uint32_t *) DEV_SDRAM_BANK_ADDR;

    for (i = 0; i < (size / 8); i++)
    {
        value = *pBuf++;
        value = *pBuf++;
        value = *pBuf++;
        value = *pBuf++;
        value = *pBuf++;
        value = *pBuf++;
        value = *pBuf++;
        value = *pBuf++;
    }
    rt_kprintf("read end tick: %d\r\n", t_end = HAL_GetTick());
    rt_kprintf("read spend time: %dms\r\n", (t_end - t_start));
    rt_kprintf("read speed: %dMbyte/s\r\n", ((32 * 1000) / (t_end - t_start)));

    rt_kprintf("=== SDRAM TEST === start data verify\r\n");
    pBuf = (uint32_t *) DEV_SDRAM_BANK_ADDR;

    for (i = 0; i < size; i++)
    {
        value = *pBuf++;

        if((value - last_value) != 1)
        {
            break;
        }

        last_value = value;
    }

    if (i < size)
    {
        rt_kprintf("read sdram error, error position: %d\r\n", i);
    }
    else
    {
        rt_kprintf("read sdram true\r\n");
    }

    return;
}

void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef* hsdram)
{
  /* USER CODE BEGIN SDRAM_MspInit 0 */

  /* USER CODE END SDRAM_MspInit 0 */
    GPIO_InitTypeDef GPIO_Initure;
    
    __HAL_RCC_FMC_CLK_ENABLE();                 //使能FMC时钟
    __HAL_RCC_GPIOC_CLK_ENABLE();               //使能GPIOC时钟
    __HAL_RCC_GPIOD_CLK_ENABLE();               //使能GPIOD时钟
    __HAL_RCC_GPIOE_CLK_ENABLE();               //使能GPIOE时钟
    __HAL_RCC_GPIOF_CLK_ENABLE();               //使能GPIOF时钟
    __HAL_RCC_GPIOG_CLK_ENABLE();               //使能GPIOG时钟
    
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3;  
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用
    GPIO_Initure.Pull=GPIO_PULLUP;              //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;         //高速
    GPIO_Initure.Alternate=GPIO_AF12_FMC;       //复用为FMC    
    HAL_GPIO_Init(GPIOC,&GPIO_Initure);          //初始化PC0,2,3
    
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14|GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOD,&GPIO_Initure);     //初始化PD0,1,8,9,10,14,15
    
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10| GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOE,&GPIO_Initure);     //初始化PE0,1,7,8,9,10,11,12,13,14,15
    
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOF,&GPIO_Initure);     //初始化PF0,1,2,3,4,5,11,12,13,14,15
    
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOG,&GPIO_Initure);      //初始化PG0,1,2,4,5,8,15 
  /* USER CODE BEGIN SDRAM_MspInit 1 */

  /* USER CODE END SDRAM_MspInit 1 */
}
